#include "elastic.h"

#include <cpr/cpr.h>

#include <iostream>
#include <memory>

#include "cjson.h"
#include "log.h"

namespace chat {
// ESIndex

ESIndex::ESIndex(std::shared_ptr<elasticlient::Client> client,
                 const std::string& index_name, const std::string& index_type)
    : client_(client), index_name_(index_name), index_type_(index_type) {
    Json::Value analysis;
    Json::Value analyzer;
    Json::Value ik;
    Json::Value tokenizer;

    tokenizer["tokenizer"] = "ik_max_word";
    ik["ik"] = tokenizer;
    analyzer["analyzer"] = ik;
    analysis["analysis"] = analyzer;
    index_["settings"] = analysis;
}

bool ESIndex::append(const std::string& key, const std::string& type,
                     const std::string& analyzer, bool enabled) {
    Json::Value fields;
    fields["type"] = type;
    fields["analyzer"] = analyzer;
    if (enabled == false) {
        fields["enabled"] = enabled;
    }
    properties_[key] = fields;

    return true;
}

bool ESIndex::create() {
    // 1. 构造 index 索引
    Json::Value mappings;
    mappings["dynamic"] = true;
    mappings["properties"] = properties_;
    index_["mappings"] = mappings;

    std::string body;
    if (JsonUtil::serialize(index_, &body) == false) {
        LOG_ERROR("序列化索引失败");
        return false;
    }

    // LOG_DEBUG("序列化index成功:\n {}", body);

    // 2. 发起构造 index 请求
    try {
        cpr::Response resp =
            client_->index(index_name_, index_type_, "default-id", body);
        if (resp.status_code < 200 || resp.status_code >= 300) {
            LOG_ERROR("索引创建失败: {}-{}", index_name_, resp.status_code);
            return false;
        }

    } catch (const std::exception& e) {
        LOG_ERROR("索引创建失败: {}-{}", index_name_, e.what());
        return false;
    }

    LOG_INFO("索引创建成功: {}", index_name_);

    return true;
}

// -------------------------------------------------------------------

// ESInsert

ESInsert::ESInsert(std::shared_ptr<elasticlient::Client> client,
                   const std::string& index_name, const std::string& index_type)
    : client_(client), index_name_(index_name), index_type_(index_type) {}

bool ESInsert::append(const std::string& key, const std::string& val) {
    item_[key] = val;

    return true;
}

bool ESInsert::insert(const std::string& id) {
    std::string body;
    if (JsonUtil::serialize(item_, &body) == false) {
        LOG_ERROR("序列化item失败");
        return false;
    }

    // LOG_DEBUG("序列化item成功:\n {}", body);

    // 2. 发起构造 index 请求
    try {
        cpr::Response resp = client_->index(index_name_, index_type_, id, body);
        if (resp.status_code < 200 || resp.status_code >= 300) {
            LOG_ERROR("数据添加失败: {}-{}", body, resp.status_code);
            return false;
        }

    } catch (const std::exception& e) {
        LOG_ERROR("数据添加失败: {}-{}", body, e.what());
        return false;
    }

    // LOG_INFO("数据添加成功: {}", body);

    return true;
}

// -------------------------------------------------------------------

// ESRemove

ESRemove::ESRemove(std::shared_ptr<elasticlient::Client> client,
                   const std::string& index_name, const std::string& index_type)
    : client_(client), index_name_(index_name), index_type_(index_type) {}

bool ESRemove::remove(const std::string& id) {
    // 2. 发起构造 index 请求
    try {
        cpr::Response resp = client_->remove(index_name_, index_type_, id);
        if (resp.status_code < 200 || resp.status_code >= 300) {
            LOG_ERROR("数据删除失败: {}", resp.status_code);
            return false;
        }

    } catch (const std::exception& e) {
        LOG_ERROR("数据删除失败: {}", e.what());
        return false;
    }

    LOG_INFO("数据删除成功: {}", id);

    return true;
}

// -------------------------------------------------------------------

// ESSearch

ESSearch::ESSearch(std::shared_ptr<elasticlient::Client> client,
                   const std::string& index_name, const std::string& index_type)
    : client_(client), index_name_(index_name), index_type_(index_type) {}

bool ESSearch::appendMustNotTerm(const std::string& key,
                                 const std::vector<std::string>& vals) {
    Json::Value fields;
    for (auto& val : vals) {
        fields[key].append(val);
    }

    Json::Value terms;
    terms["terms"] = fields;
    must_not_.append(terms);

    return true;
}

bool ESSearch::appendShouldMatch(const std::string& key,
                                 const std::string& val) {
    Json::Value fields;
    fields[key] = val;
    Json::Value match;
    match["match"] = fields;
    should_.append(match);

    return true;
}

bool ESSearch::appendMustTerm(const std::string& key, const std::string& val) {
    Json::Value fields;
    fields[key] = val;
    Json::Value term;
    term["term"] = fields;
    must_.append(term);

    return true;
}

bool ESSearch::appendMustMatch(const std::string& key, const std::string& val) {
    Json::Value fields;
    fields[key] = val;
    Json::Value match;
    match["match"] = fields;
    must_.append(match);

    return true;
}

Json::Value ESSearch::search() {
    // 1. 构造 search 请求
    Json::Value cond;
    if (must_not_.empty() == false) {
        cond["must_not"] = must_not_;
    }
    if (should_.empty() == false) {
        cond["should"] = should_;
    }
    if (must_.empty() == false) {
        cond["must"] = must_;
    }
    Json::Value query;
    query["bool"] = cond;
    Json::Value root;
    root["query"] = query;

    std::string body;
    if (JsonUtil::serialize(root, &body) == false) {
        LOG_ERROR("序列化请求失败");
        return false;
    }

    // LOG_DEBUG("序列化请求成功:\n {}", body);

    // 2. 发起构造 index 请求
    cpr::Response resp;
    try {
        resp = client_->search(index_name_, index_type_, body);
        if (resp.status_code < 200 || resp.status_code >= 300) {
            LOG_ERROR("搜索请求失败: {}-{}", body, resp.status_code);
            return Json::Value();
        }

    } catch (const std::exception& e) {
        LOG_ERROR("搜索请求失败: {}-{}", body, e.what());
        return Json::Value();
    }

    // LOG_INFO("搜索请求成功: {}", body);

    // 3. 反序列化响应
    if (JsonUtil::unSerialize(&root, resp.text) == false) {
        LOG_ERROR("响应反序列化失败: {}", resp.text);
        return Json::Value();
    }

    return root["hits"]["hits"];
}
}  // namespace chat
